<template>
  <div ref="html2canvas" id="qaOnline" class="online-editor">
    <div class="header-toobox">
      <el-row type="flex">
        <el-col :span="8" :xs="24">
          <remark-form :frame="currentFrame" :index="currentIndex"></remark-form>
        </el-col>

        <el-col :span="8" :xs="24">
          <div class="header-tool-box">
            <el-input
              v-if="!!config.track"
              placeholder="TrackID"
              v-model="selectedObject.a1"
              size="mini"
              class="input-search"
            >
              <template slot="append">
                <el-button
                  type="primary"
                  icon="el-icon-search"
                  @click="sync_highlight_objects('', selectedObject, undefined)"
                  >查找</el-button
                >
              </template>
            </el-input>

            <el-tooltip effect="dark" content="保存，快捷键（ctrl + s）" placement="bottom">
              <el-button circle type="primary" size="mini" icon="fa fa-floppy-o" @click="submitAnnotations" />
            </el-tooltip>

            <el-tooltip effect="dark" content="批注列表" placement="bottom">
              <el-button
                type="primary"
                size="mini"
                @click="
                  () => {
                    remarkListVisable = !remarkListVisable
                    this.$refs.remarkList.show(remarkListVisable)
                  }
                "
                icon="el-icon-notebook-2"
                circle
              />
            </el-tooltip>

            <el-tooltip effect="dark" v-show="commonConfig.fusionView" content="融合浏览" placement="bottom">
              <el-button
                type="primary"
                size="mini"
                @click="
                  () => {
                    $router.push({
                      path: '/mark/RadarView',
                      query: {
                        taskId: queryParams.taskId,
                        mQaResultId: currentFrame.mQaResultId,
                      },
                    })
                  }
                "
                icon="el-icon-video-camera-solid"
                circle
              />
            </el-tooltip>

            <!-- <el-tooltip effect="dark" content="自动标注" placement="bottom">
                <el-button type="primary" size="mini" icon="el-icon-edit-outline" @click="startAutoAnnotation()" style="margin-left: 0;" circle />
              </el-tooltip> -->

            <el-tooltip
              v-if="checkrole(['admin', 'checker', 'marker'])"
              effect="dark"
              content="复制上一帧的标注结果，快捷键（shift + v）"
              placement="bottom"
            >
              <el-button
                :disabled="currentIndex <= 0"
                type="primary"
                size="mini"
                icon="el-icon-document-copy"
                @click="copyPreviousAnnotation()"
                circle
              />
            </el-tooltip>
          </div>
        </el-col>

        <el-col :span="8" :xs="24">
          <div class="header-frame-info">
            <div class="info-item" v-if="currentFrame && currentFrame.updateBy && !checkrole(['qa'])">
              <label> 标注员：</label>
              <span>{{ currentFrame.updateBy }}</span>
            </div>
            <div class="info-item" v-if="currentFrame && currentFrame.updateTime && !checkrole(['qa'])">
              <label for="">更新时间：</label>
              <span>{{ currentFrame.updateTime }}</span>
            </div>
          </div>
        </el-col>
        <el-col :span="1" :xs="24">
          <el-tooltip effect="dark" :content="toolbar.fullscreen ? '退出全屏' : '全屏'" placement="bottom">
            <el-button
              type="primary"
              size="mini"
              @click="handleFullScreen()"
              :icon="toolbar.fullscreen ? 'el-icon-aim' : 'el-icon-full-screen'"
              circle
            />
          </el-tooltip>
          <el-tooltip effect="dark" content="返回文件列表" placement="bottom">
            <el-button type="primary" icon="el-icon-d-arrow-left" size="mini" @click="returnFileList()" circle />
          </el-tooltip>
        </el-col>
      </el-row>
    </div>

    <div class="content-container">
      <SliderContainer v-on:content-resize="onWindowResize">
        <template slot="left">
          <m-image-marker
            v-if="config.imageCount"
            ref="pictureList"
            :frame="currentFrame"
            :task="task"
            :images="images"
            :annotations="viewAnnotations"
            :update-property="updateAnnotationPorperty"
            :selected="selectedAnno"
            @selected-change="handleSelectedAnnoChange"
            @update="handleAnnotationChange"
            v-bind:height="83"
          >
          </m-image-marker>
        </template>
        <template slot="right">
          <point-cloud
            v-if="config.pcdCount"
            ref="pointCloud"
            v-bind:height="83"
            v-bind:task-id="queryParams.taskId"
            v-bind:hide-tip="HideTip"
            v-bind:show-tip="ShowTip"
            v-bind:attribute-names="attributeNames"
            v-bind:render-type="view3D.renderType"
            v-bind:camera-position="view3D.cameraPosition"
            v-bind:high-light-cube-points="view3D.highLightCubePoints"
            v-bind:color-names="colorNames"
            v-bind:loading.sync="loading"
            v-bind:sync_highlight_objects="sync_highlight_objects"
            v-bind:sync_clear_highlight_objects="sync_clear_highlight_objects"
            v-bind:change-page="changePage"
            v-bind:set-position="setPosition"
            v-bind:elements-remark="elementsRemark"
            v-bind:handle-element-comments="remarkFormOpen"
            v-bind:change-cube-info="changeCubeInfo"
            v-bind:add-split-mark-annotation="addSplitMarkAnnotation"
          ></point-cloud>
        </template>
      </SliderContainer>
    </div>

    <!-- 底部 -->
    <file-list-pagination
      :changeBefore="frameChangeBefore"
      :files="fileList"
      :value="currentIndex"
      :percent="cacheStatus.percent"
      @change="onFrameChange"
    >
      <template slot="extend-left">
        <span>{{ task.path }}</span>
      </template>
      <template slot="extend-right">
        <span>{{ currentFrame.fileId }}</span>
        <!-- 标注列表 -->
        <annotation-list-box
          :data="annotationList"
          :update-property="updateAnnotationPorperty"
          :selected="selectedAnno"
          @selected-change="handleSelectedAnnoChange"
          @selection-change="handleSelectionAnnoChange"
          @update="handleAnnotationChange"
        ></annotation-list-box>
      </template>
    </file-list-pagination>

    <div
      class="special_focus_toolTip"
      :style="{ left: detailTooltip.x + 'px', top: detailTooltip.y + 'px' }"
      v-show="detailTooltip.show"
      v-html="tooltipBody"
    />

    <!-- 批注列表 -->
    <RemarkList
      ref="remarkList"
      :task-id="queryParams.taskId"
      :set-position="setPosition"
      :delete-remark="deleteRemark"
      :update-remark="updateRemark"
    />

    <!-- 批注属性 -->
    <comment-dialog ref="commentDialog" @onOk="handleElementComments" />

    <!-- annotation list -->
    <property-dialog ref="propertyDialog" :attributeNames="attributeNames" @submit="handleAnnotationChange" />

    <!-- 点云属性预览 -->
    <cube-attribute
      v-bind:attrs-view-drawer="attrsViewDrawer"
      v-bind:selected-object="selectedObject"
      v-bind:cube-info="cubeInfo"
    />

    <pcd-object-dialog
      ref="pcdObjectDialog"
      :visible.sync="updateSplitInfoDialogOpen"
      @onOk="submitUpdateSplitInfoForm"
    />
  </div>
</template>

<script>
// http://localhost:3000/#/editor2d?taskId=7770&mQaResultId=300357057
// TODO: getMarkAnnotations 接口bug修复
import { getMarkAnnotations, pointsCloudDetails, getImageWithCache, pcdBlob } from '@/api/mark/onlineCheck'
import {
  annotationList,
  addAnnotation,
  updateAnnotation,
  updateAnnotationAttributes,
  deleteAnnotation,
  autoAnnotation,
  copyPreviousAnnotation,
} from '@/api/mark/markAnnotation'
import { listResult, updateResult } from '@/api/mark/qaresult'
import { getTaskWithParent } from '@/api/mark/qaTask'
import { listRemarkBall, delRemarkBall, addRemarkBall } from '@/api/mark/remarkBall'

import screenfull from 'screenfull'
import { checkRole } from '@/utils/permission'
import MImageMarker from './components/MImageMarker'
import PointCloud from './components/PointCloud'
import SliderContainer from './components/SliderContainer'
import RemarkList from './components/RemarkList'
import RemarkForm from '@/views/mark/meditor/modules/RemarkForm'
import FileListPagination from '@/views/mark/meditor/modules/FileListPagination'
import CubeAttribute from './components/CubeAttribute'
import html2canvas from 'html2canvas'
import * as Whammy from './whammy.js'

import ConfigBridge from '@/views/mark/meditor/ConfigBridge'
import { Annotation, ActionStat } from './components/MImageMarker/data/annotation'

import CommentDialog from './modules/CommentDialog'
import PropertyDialog from './modules/PropertyDialog'
import PcdObjectDialog from './modules/PcdObjectDialog'
import AnnotationListBox from './components/AnnotationListBox.vue'

/**
 * 数据内容
1.任务 task
2.标注 annotation
3.批注 remark: 帧 框
4.文件 frame
 */
export default {
  name: 'FuseEditor',
  components: {
    SliderContainer,
    MImageMarker,
    PointCloud,
    RemarkList,
    RemarkForm,
    FileListPagination,
    CubeAttribute,

    AnnotationListBox,

    CommentDialog,
    PropertyDialog,
    PcdObjectDialog,
  },
  computed: {
    markAnnotations() {
      return this.$store.state.markData.markAnnotations
    },
    view3D() {
      return this.config?.view3D || {}
    },
    commonConfig() {
      return this.config?.common || {}
    },
  },

  provide() {
    return { getConfig: this.getConfig }
  },
  data() {
    return {
      task: {},
      config: {},
      queryParams: {},

      toolbar: {
        fullscreen: false,
      },

      fileList: [],
      currentFrame: {},
      currentIndex: -1,
      cacheStatus: {
        taskId: null,
        index: 0,
        started: false,
        version: 0,
        percent: 0,
      },

      // 当前帧标注框
      images: [],
      selectedIds: [], // 选择的标注IDs
      annotationList: [],
      viewAnnotations: [],
      selectedAnno: null,
      changeList: new Set(),

      showAttributesText: true,
      animationFrameId: undefined,
      detailsDialogOpen: false,

      selectedObject: {
        a1: '',
        remark: '',
        attributes: [],
        type: '',
        imageIndex: '',
        errorType: '',
        mPoint: { x: -1, y: -1, z: -1 },
        elementInstance: null,
        position: null,
      },
      cubeInfo: { width: '', height: '', length: '' },
      colorNames: {},
      attributeNames: {},
      // imgCount: 0,
      // markType: 0,
      remarkMap: new Map(), // 批注图标
      loading: false,

      tooltipBody: '',
      detailTooltip: { show: false, x: 0, y: 0 },

      elementsRemark: {},
      selectedObjectA1: null,
      attrsViewDrawer: false,

      updateSplitInfoDialogOpen: false,

      updateSplitInfoForm: {
        mMarkAnnotationId: undefined,
      },
      remarkListVisable: false,

      playStarted: false,
      lastPlayTime: null,
      hideOther: false,

      encoder: null,
    }
  },
  methods: {
    checkrole(role) {
      return checkRole(role)
    },

    getConfig() {
      return this.config
    },

    // 批注列表点击后图片和点云矩形缩放定位
    setPosition(a1, mQaResultId, remark, type, imageIndex, errorType, point, position) {
      if (!a1 && !position) return
      this.selectedObjectA1 = a1
      if (this.$route.query.index) {
        if (parseInt(this.$route.query.index) - this.currentIndex - 1 != 0)
          this.changePage(parseInt(this.$route.query.index) - this.currentIndex - 1)
      }

      if (!this.config.track) return

      this.selectedObject.a1 = a1
      this.selectedObject.remark = remark
      this.selectedObject.type = type
      this.selectedObject.imageIndex = imageIndex
      this.selectedObject.errorType = errorType
      this.selectedObject.position = position
      if (type == '2d') {
        this.selectedObject.attributes = this.$refs.pictureList.getSelectedObjectAttributes(a1, imageIndex)
      } else if (type == '3d') {
        this.selectedObject.attributes = this.$refs.pointCloud.getSelectedObjectAttributes(a1)
      }
      this.sync_highlight_objects('', this.selectedObject)
    },

    // 批注列表删除/批注后更新/图片和点云显示的批注图标
    deleteRemark(a1, type, elementsRemarkKey) {
      this.removeFrameCache(this.fileList[this.currentIndex].mQaResultId)
      this.changePage(0)
      // 这里处理重新打开批注弹框时的remark input值
      // if (this.selectedObject.a1 == a1 && type && this.selectedObject.type == type) {
      //   this.selectedObject.remark = ''
      // }

      // // 删除 this.elementsRemark 对应的记录
      // let item = this.remarkMap.get(elementsRemarkKey);
      // if (item && type == '2d') {
      //   item.remove();
      //   item.markRemarkId && item.textObject.remove();
      // }

      // this.elementsRemark[elementsRemarkKey] = null;
      // this.elementsRemark['ball-' + elementsRemarkKey] = null;
      // this.remarkMap.delete(elementsRemarkKey);
      // if(type === "3d")
      //   this.$refs.pointCloud.reRender();
      // if(type === "2d")
      //   this.$refs.pictureList.reRender();
    },
    // 批注列表更新批注后更新缓存批注数据
    updateRemark(a1, type, elementsRemarkKey, remark) {
      // 这里处理重新打开批注弹框时的remark input值
      if (this.selectedObject.a1 == a1 && type && this.selectedObject.type == type) {
        this.selectedObject.remark = remark
      }
      this.elementsRemark[elementsRemarkKey] = remark
      if (this.elementsRemark['ball-' + elementsRemarkKey]) {
        this.elementsRemark['ball-' + elementsRemarkKey].remark = remark
      }

      // if(type === "3d")
      //   this.$refs.pointCloud.reRender();
      // if(type === "2d")
      //   this.$refs.pictureList.reRender();
    },

    remarkFormOpen(missId, selectedObject) {
      this.selectedObject = selectedObject
      if (this.selectedObject) {
        this.HideTip()

        this.detailsDialogOpen = true
        this.selectedObject.remark = '验'

        this.$refs.commentDialog.open(this.selectedObject)

        let detailRemarkInput = document.getElementById('detailRemarkInput')
        if (detailRemarkInput) detailRemarkInput.focus()
      }
    },
    // 更新批注信息（新增或删除批注）
    handleElementComments({ id, remark }) {
      let remarkBall = {
        taskId: this.fileList[this.currentIndex].taskId,
        mQaResultId: this.fileList[this.currentIndex].mQaResultId,
        fileName: this.fileList[this.currentIndex].fileId,
        trackId: id,
        mType:
          this.selectedObject.type + (this.selectedObject.type == '2d' ? '-' + this.selectedObject.imageIndex : ''),
        errorType: this.selectedObject.errorType,
        remark: remark,
        x: this.selectedObject.mPoint.x,
        y: this.selectedObject.mPoint.y,
        z: this.selectedObject.mPoint.z,
      }
      addRemarkBall(remarkBall).then((response) => {
        if (response.code === 200) {
          if (this.selectedObject.callback) {
            this.selectedObject.callback('success', response.markRemarkId)
          }
          if (this.selectedObject.elementInstance)
            this.selectedObject.elementInstance.markRemarkId = response.markRemarkId
          // if(this.markType === 10) {
          this.removeFrameCache(this.fileList[this.currentIndex].mQaResultId)
          this.changePage(0)
          this.remarkListVisable = false
          this.$refs.remarkList.show(this.remarkListVisable)
          // }
          // this.msgSuccess("更新成功");
        } else {
          this.msgError(response.msg)
          if (this.selectedObject.callback) {
            this.selectedObject.callback('fail')
          }
        }
      })
      // if(remark && remark !== "") {
      //   if(id) {

      //   }
      //   else{
      //     this.msgInfo("未选中目标。");
      //   }
      // }
      // else {
      //   delRemarkBall(this.selectedObject.elementInstance.markRemarkId).then(response => {
      //     if (response.code === 200) {
      //       this.msgSuccess("删除成功");
      //     } else {
      //       this.msgError(response.msg);
      //     }
      //   });
      // }
      // if(id == null || id == "" || id == undefined)
      //   return;
      // if(!id.startsWith("lou-") && parseInt(this.markType) !== 10) {
      // if(this.selectedObject.a1) {
      //   id = id + '-' + this.selectedObject.type + (this.selectedObject.imageIndex != '' ? '-' + this.selectedObject.imageIndex : '');
      //   this.elementsRemark[id] = remark;
      //   this.elementsRemark['ball-' + this.selectedObject.a1 + "-" + remarkBall.mType] = this.selectedObject.errorType;
      //   this.sync_highlight_objects("", this.selectedObject);
      // }
    },

    // 翻页函数，上一页、下一页翻页，页码跳转、选中批注后跳转页码
    frameChangeBefore(index, frame) {
      console.log(this.changeList)
      if (this.changeList.size > 0) {
        return this.$confirm('当前页面存在未保存的数据，是否忽略所做的修改?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'email',
        })
      } else {
        return true
      }
    },
    async onFrameChange(index, current) {
      this.currentIndex = index
      this.currentFrame = current
      const query = {
        taskId: this.queryParams.taskId,
        mQaResultId: current.mQaResultId,
      }
      // load images / annotations
      const { imageCount } = this.config
      this.images = await getImageWithCache(query.mQaResultId, imageCount)
      this.queryAnnotationList(query.mQaResultId)
      // console.log(this.images)
      const { path } = this.$route
      this.$router.replace({ path, query })
      this.$store.dispatch('mqaResultCurrentIndexCached', this.currentIndex)

      // 开始缓存
      this.cacheStatus.index = index
      this.frameFileCache()
    },

    /**
     * toolbox
     * *********************************************
     */

    // 页面全屏函数
    handleFullScreen() {
      this.toolbar.fullscreen = !this.toolbar.fullscreen
      try {
        if (this.toolbar.fullscreen) {
          screenfull.request(document.getElementById('qaOnline'))
        } else {
          screenfull.toggle()
        }
      } catch (error) {
        console.log(error)
      }
    },
    // 返回到质检结果记录列表页面
    returnFileList(e) {
      // history.back();
      history.pushState(null, null, document.URL)
      if (e) e.preventDefault()
      this.$router.replace({
        path: '/mark/result',
        query: { taskId: this.queryParams.taskId },
      })
    },
    // document 快捷键监听函数,全局键盘事件处理,input事件在对应的元素上直接阻止冒泡
    keyDownHandler(e) {
      const { config } = this
      const activeElement = document.activeElement
      console.log(activeElement, e.target.nodeName)

      const skipClass = 'skip-shortcuts'
      if (activeElement.classList.contains(skipClass) || activeElement.parentElement.classList.contains(skipClass)) {
        return
      }

      // 批注输入框
      if (document.activeElement.id == 'remarkInput') {
        if (e.keyCode == 13 || e.keyCode == 32) {
          e.preventDefault()
        }
        return
      }
      // 注解对话框
      if (document.activeElement.id == 'detailRemarkInput') {
        if (e.keyCode == 13 || e.keyCode == 32) {
          e.preventDefault()
          if (e.keyCode == 13 && this.selectedObject && this.selectedObject.remark) {
            this.handleElementComments(
              this.selectedObject.a1 ? this.selectedObject.a1 + '' : this.selectedObject.mMarkAnnotationId + '',
              this.selectedObject.remark
            )
          }
        }
        return
      }
      // trackId 查找
      if (document.activeElement.id == 'trackID') {
        if (e.keyCode == 13 && this.selectedObject && this.selectedObject.a1) {
          this.sync_highlight_objects('', this.selectedObject)
        }
      }

      if (e.keyCode == 13) {
        e.preventDefault()
        return
      }

      if (e.altKey && e.ctrlKey) {
        this.showAttributesText = !this.showAttributesText
        this.$refs.pictureList.showAttributesText2D(this.showAttributesText)
        if (config.pcdCount) this.$refs.pointCloud.showAttributesText2D(this.showAttributesText)
        e.preventDefault()
        return
      }
      if (e.ctrlKey && e.shiftKey) {
        // if(this.selectedObject && this.selectedObject.a1) {
        this.hideOther = !this.hideOther
        // }
        // else {
        //   this.hideOther = false;
        // }
        this.$refs.pictureList.highlight_objects(undefined, this.selectedObject, false, this.hideOther)
        if (config.pcdCount)
          this.$refs.pointCloud.highlight_objects(undefined, this.selectedObject, false, this.hideOther)
      }
      if (e.ctrlKey && e.key.toLowerCase() === 's') {
        e.preventDefault()
        this.submitAnnotations()
        return
      }
      if (e.shiftKey && e.key.toLowerCase() === 'v') {
        this.copyPreviousAnnotation()
        return
      }

      if (e.key.toLowerCase() === 's') {
        this.$refs.pictureList.paperToolChange('rectPoint1')
        return
      } else if (e.key.toLowerCase() === 'r') {
        this.$refs.pictureList.paperToolChange('rect')
        return
      } else if (e.key.toLowerCase() === 'q') {
        this.$refs.pictureList.paperToolChange('select')
        this.$refs.pointCloud.changeLabelTool('view')
        return
      } else if (e.key.toLowerCase() === 'w') {
        this.$refs.pointCloud.changeLabelTool(null)
        return
      }

      // e
      if (e.keyCode == 69) {
        e.preventDefault()
        this.remarkFormOpen(this.selectedObject.a1, this.selectedObject)
      }
      // t
      else if (e.keyCode == 84) {
        e.preventDefault()
        let remarkInput = document.getElementById('remarkInput')
        remarkInput.focus()
      }
      // q
      else if (e.keyCode == 81) {
        history.back()
      }
      // f
      else if (e.keyCode == 70) {
        if (!this.toolbar.fullscreen) {
          this.handleFullScreen()
        }
      }
      // a
      else if (e.keyCode == 65) {
        if (this.currentIndex >= 1 && this.loading !== true) {
          this.currentIndex--
        }
      }
      // d
      else if (e.keyCode == 68) {
        if (this.currentIndex < this.fileList.length - 1 && this.loading !== true) {
          this.currentIndex++
        }
      }
      // // s
      // else if(e.keyCode == 83 && this.loading !== true) {
      //   this.handleComments('通过');
      // }
      // // w
      // else if(e.keyCode == 87 && this.loading !== true) {
      //   this.handleComments('驳回');
      // }
      // // r
      // else if(e.keyCode == 82 && this.loading !== true) {
      //   this.handleComments('挂起');
      // }
      // ALT + x
      else if (e.keyCode == 88 && e.altKey) {
        // 属性修改
        // const annotation = this.selectedObject
        const annotation = {}
        this.updateAnnotationPorperty(annotation)
      }
    },

    // 查询验收任务信息，包含属性类型、颜色类型等基础字典数据
    async qaTaskQuery() {
      const { taskId } = this.queryParams
      const task = await getTaskWithParent(taskId)
      this.task = task

      const configKey = task.markAttributes
      let markAttributes = null,
        colorNames = null
      try {
        markAttributes = await this.$getItem(configKey)
        colorNames = await this.$getItem(configKey + '_colors')
        if (!markAttributes || !colorNames) {
          throw new Error('no cache')
        }
        console.log('Load Config from cache')
      } catch (error) {
        const { data: markAttributesNet } = await this.getDicts(configKey)
        const { data: colorNamesNet } = await this.getDicts(configKey + '_colors')
        markAttributes = markAttributesNet
        colorNames = colorNamesNet

        await this.$setItem(configKey, markAttributes)
        await this.$setItem(configKey + '_colors', colorNames)
      }
      if (!markAttributes || !colorNames) console.error('Load Config ERROR')

      // 配置
      const config = new ConfigBridge(task, markAttributes, colorNames)
      this.config = Object.freeze(config)
      console.log(config)

      this.attributeNames = markAttributes
      this.colorNames = colorNames
      this.loadFrameAndRemarkList()
    },

    // 获取质检结果记录数据列表，并调用图片列表和点云视图的渲染函数进行页面渲染
    async loadFrameAndRemarkList() {
      const { taskId, mQaResultId } = this.queryParams
      const { rows: fileList, total } = await listResult(this.queryParams)

      this.fileList = fileList
      for (let i = 0; i < total; i++) {
        const fileItem = fileList[i]
        if (mQaResultId == null || mQaResultId == fileItem.mQaResultId) {
          this.queryParams.mQaResultId = fileItem.mQaResultId
          this.currentIndex = i
          this.$store.dispatch('mqaResultCurrentIndexCached', i)
          break
        }
      }

      // this.frameFileCache(taskId)
      try {
        // getMarkAnnotations(taskId).then((res) => {
        //   this.$store.dispatch('markAnnotationsCache', res.annotations)
        // })
        listRemarkBall({ taskId }).then((res) => {
          this.$store.dispatch('remarkListCache', res.rows)
        })
      } catch (e) {
        console.error('get remarkListCache Exception: ' + e)
      }
    },

    // 文件缓存
    async frameFileCache() {
      const { cacheStatus, fileList, currentIndex, task } = this
      const { pcdCount, imageCount } = this.config
      // let { version, percent } = cacheStatus
      const taskId = task.taskId
      cacheStatus.started = true
      cacheStatus.taskId = taskId
      cacheStatus.percent = 0
      cacheStatus.version++
      const version = cacheStatus.version

      const allCacheKeys = await this.$keysInStorage()

      while (cacheStatus.index < currentIndex + 20 && fileList[cacheStatus.index]) {
        if (version !== cacheStatus.version) {
          break
        }
        console.log('frameFileCache version: ', cacheStatus.version)

        const resultId = fileList[cacheStatus.index].mQaResultId
        try {
          if (pcdCount) {
            if (!allCacheKeys.includes('MP:' + resultId)) {
              const response = await pcdBlob(resultId)
              const blob = new Blob([response.data], { type: 'application/octet-stream' })
              const _data = { blob: blob, file_name: resultId }
              await this.$setItem('MP:' + resultId, _data)
            }
          }
          imageCount && getImageWithCache(resultId, imageCount)
        } catch (error) {
          console.error(error)
        } finally {
          console.log('[' + /\d\d\:\d\d\:\d\d/.exec(new Date())[0] + ']', 'save ' + fileList[cacheStatus.index].fileId)
          cacheStatus.index++
          cacheStatus.percent = parseInt((cacheStatus.index * 100) / fileList.length)
        }
      }
      cacheStatus.started = false
    },
    removeFrameCache(qaResultId) {
      // this.markAnnotations.delete('MA:' + qaResultId + '')
      const { imageCount, pcdCount } = this.config
      if (imageCount) {
        for (let j = 0; j < imageCount; j++) {
          this.$removeItem('MA:' + qaResultId + ':img:' + j)
        }
      }
      pcdCount && this.$removeItem('MP:' + qaResultId)
    },

    // 图片列表和点云视图的公共定位缩放函数
    sync_highlight_objects(eventSource, selectedObject, cubeInfo, isZoom = true) {
      const { imageCount, pcdCount } = this.config
      this.selectedObject = selectedObject
      this.cubeInfo = cubeInfo
      this.hideOther = false
      if (imageCount) this.$refs.pictureList.highlight_objects(eventSource, selectedObject, isZoom, this.hideOther)
      if (pcdCount) this.$refs.pointCloud.highlight_objects(eventSource, selectedObject, isZoom, this.hideOther)
    },
    sync_clear_highlight_objects(selectedObject) {
      this.selectedObject = selectedObject
      const { imageCount, pcdCount } = this.config
      if (imageCount) this.$refs.pictureList.clear_highlight_objects()
      if (pcdCount) this.$refs.pointCloud.clear_highlight_objects()
    },
    // 更新右侧属性信息
    changeCubeInfo(cubeInfo) {
      this.cubeInfo = cubeInfo
    },

    HideTip() {
      this.detailTooltip.show = false
    },
    // 显示标注矩形的属性弹框信息

    ShowAttributesText(show) {
      let opacity = show ? 1 : 0
      for (let m = 0; m < this.mainLayer.length; m++) {
        if (!this.mainLayer[m]) continue
        let items = this.mainLayer[m].getItems()
        for (let i = 0; i < items.length; i++) {
          if (items[i].textObject) {
            items[i].textObject.opacity = opacity
          }
        }
      }
    },
    contextmenuHandler(e) {
      e.preventDefault()
    },
    onWindowResize() {
      // this.$refs.pointCloud.onWindowResize()
      // this.$refs.pictureList.onWindowResize()
    },

    //图片格式转换
    dataURLToBlob(dataurl) {
      let arr = dataurl.split(',')
      let mime = arr[0].match(/:(.*?);/)[1]
      let bstr = atob(arr[1])
      let n = bstr.length
      let u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new Blob([u8arr], { type: mime })
    },
    saveImage(divText, imgText) {
      let canvasID = this.$refs[divText]
      // let that = this;
      // let a = document.createElement('a');
      html2canvas(canvasID).then((canvas) => {
        // let dom = document.body.appendChild(canvas);
        // dom.style.display = 'none';
        // a.style.display = 'none';
        // document.body.removeChild(dom);
        // let blob = that.dataURLToBlob(dom.toDataURL('image/png'));
        // let url = URL.createObjectURL(blob);
        this.encoder.add(canvas)
        // a.setAttribute('href', url);
        // //这块是保存图片操作  可以设置保存的图片的信息
        // a.setAttribute('download', imgText + '.png');
        // document.body.appendChild(a);
        // a.click();
        // URL.revokeObjectURL(blob);
        // document.body.removeChild(a);
      })
    },
    animation() {
      if (this.playStarted) {
        if (performance.now() - this.lastPlayTime > 50) {
          this.playFunction()
        }
        window.requestAnimationFrame(this.animation)
      }
    },
    startPlay() {
      this.playStarted = true
      this.lastPlayTime = performance.now()
      if (this.interval === undefined) {
        this.interval = window.requestAnimationFrame(this.animation)
      }
    },
    pausePlay() {
      this.playStarted = false
      window.cancelAnimationFrame(this.interval)
      this.interval = undefined
      this.changePage(0)
    },
    playFunction() {
      if (this.currentIndex < this.fileList.length) {
        if (this.$store.state.markData.playStatus.pic && this.$store.state.markData.playStatus.pcd) {
          this.$store.state.markData.playStatus.pic = false
          this.$store.state.markData.playStatus.pcd = false
          this.saveImage('html2canvas', 'test')

          let readValue = this.markAnnotations.get('MA:' + this.fileList[this.currentIndex].mQaResultId + '')
          if (readValue) {
            this.$refs.pointCloud
              .getPointsCloud(
                this.fileList,
                this.currentIndex,
                this.fileList[this.currentIndex].mQaResultId,
                true,
                this.showAttributesText
              )
              .then(() => {
                let query = {
                  taskId: this.queryParams.taskId,
                  mQaResultId: this.fileList[this.currentIndex].mQaResultId,
                }
                this.$router.replace({
                  path: '/mark/editor2d',
                  query: query,
                })

                // TODO: 路由监听实现
                const { imageCount = 0 } = this.config
                if (imageCount > 0) {
                  this.$refs.pictureList.loadDrawing(
                    this.currentIndex,
                    this.fileList[this.currentIndex].mQaResultId,
                    this.showAttributesText,
                    true
                  )
                }
                if (this.currentIndex < this.fileList.length) this.currentIndex++
                this.lastPlayTime = performance.now()
              })
          }
        }
      } else {
        this.saveImage('html2canvas', 'test')
        this.encoder.compile(false, (output) => {
          const url = (window.webkitURL || window.URL).createObjectURL(output)
          let a = document.createElement('a')
          a.style.display = 'none'
          a.setAttribute('href', url)
          a.setAttribute('download', this.taskName + '_w.mp4')
          document.body.appendChild(a)
          a.click()
          URL.revokeObjectURL(output)
          document.body.removeChild(a)
        })
        this.playStarted = false
        this.currentIndex = this.fileList.length - 1
        cancelAnimationFrame(this.interval)
        this.interval = undefined
      }
    },

    // ?
    addSplitMarkAnnotation(pointsIndex, minusPointsIndex, callback) {
      this.$confirm('需要将选中的点作为新目标创建还是更新已有记录？', '提交', {
        confirmButtonText: '新建',
        cancelButtonText: '更新',
        type: 'email',
      })
        .then(() => {
          if (pointsIndex && pointsIndex.length > 0) {
            this.selectedObject = {
              name: '',
              type: '3d',
            }
            this.form.mMarkAnnotationId = null
            this.form.mQaResultId = this.fileList[this.currentIndex].mQaResultId
            this.form.name = this.selectedObject.name
            this.form.x = pointsIndex.toString()
            this.form.z = 'split'
            for (let i = 1; i <= 8; i++) {
              this.form['a' + i] = null
            }
            this.form.fileType = '3d'
            this.form.fileName = this.fileList[this.currentIndex].fileId
            this.form.callback = callback
            this.annotationInfoDialogTitle = '新建点云分割目标'

            // this.annotationInfoDialogOpen = true
            this.$refs.propertyDialog.open(this.selectedObject)
          }
        })
        .catch(() => {
          this.queryAnnotationList()
          this.updateSplitInfoForm.pointsIndex = pointsIndex
          this.updateSplitInfoForm.minusPointsIndex = minusPointsIndex
          this.updateSplitInfoForm.callback = callback
          this.updateSplitInfoDialogOpen = true
        })
    },
    submitUpdateSplitInfoForm(tobeUpdated) {
      updateAnnotation(tobeUpdated).then((response) => {
        if (response.code == 200) {
          // this.updateSplitInfoForm.callback(params);
          this.msgSuccess('更新成功')
          this.removeFrameCache(this.fileList[this.currentIndex].mQaResultId)
          this.changePage(0)
        } else {
          this.msgError(response.msg)
        }
        this.updateSplitInfoDialogOpen = false
      })
    },

    /**
     * ************************************************************
     * region start  >>> annotation
     */
    // 获取单帧的标注列表
    queryAnnotationList(mQaResultId) {
      annotationList({ mQaResultId }).then((res) => {
        let annos = []
        if (res.code == '200') {
          annos = res.rows.map((anno) => Annotation.fromData(anno))
        } else {
          this.msgError(res.msg)
        }
        this.annotationList = annos
        this.viewAnnotations = annos
      })
    },

    updateAnnotationPorperty(annotation, callback) {
      const title = (annotation.mMarkAnnotationId ? '修改' : '新建') + '标注属性'
      console.log(annotation)
      this.$refs.propertyDialog.open(annotation, { title }, callback)
    },

    handleAnnotationChange(annotation) {
      // 修改 annotation 后回调: new/update/delete
      // 提交到 server / change list
      this.changeList.add(annotation)
    },

    submitAnnotations() {
      const changeList = new Set(this.changeList)
      this.changeList.clear()
      console.log(changeList)

      for (const annotation of changeList) {
        console.log(annotation)
        if (annotation.status === ActionStat.NEW) {
        } else if (annotation.status === ActionStat.UPDATE) {
        } else if (annotation.status === ActionStat.DELETE) {
        } else {
          console.error('Unknow change type')
        }
      }

      return

      if (!annotation.mMarkAnnotationId) {
        if (annotation.type !== '3d') return
        addAnnotation(annotation).then((response) => {
          if (response.code == 200) {
            this.msgSuccess('标注成功')
            this.removeFrameCache(this.fileList[this.currentIndex].mQaResultId)
            this.changePage(0)
          } else {
            this.msgError(response.msg)
          }
        })
      } else {
        updateAnnotationAttributes(annotation).then((response) => {
          if (response.code === 200) {
            this.msgSuccess('修改成功')
            this.removeFrameCache(this.fileList[this.currentIndex].mQaResultId)
            this.changePage(0)
          } else {
            this.msgError(response.msg)
          }
        })
      }

      let changeStore = this.changeList.fetch()
      for (let key in changeStore) {
        let tmp = changeStore[key]
        if (tmp.action && tmp.action === 'update') {
          if (tmp.type === 'path' && tmp.info.mMarkAnnotationId && tmp.info.markType === 'rect') {
            let path = tmp.info
            let x = '',
              y = ''
            let tmpPoint = path.pointToLocal(path.point1.segments[0].point)
            x += tmpPoint.x + ';'
            y += tmpPoint.y + ';'
            tmpPoint = path.pointToLocal(path.point3.segments[0].point)
            x += tmpPoint.x + ';'
            y += tmpPoint.y + ';'
            if (path.midPath) {
              tmpPoint = path.pointToLocal(path.midPath.segments[0].point)
              x += tmpPoint.x + ';'
              y += tmpPoint.y + ';'
            }
            if (path.rangePath) {
              tmpPoint = path.pointToLocal(path.rangePath.segments[0].point)
              x += tmpPoint.x + ';'
              y += tmpPoint.y + ';'
              tmpPoint = path.pointToLocal(path.rangePath.segments[1].point)
              x += tmpPoint.x + ';'
              y += tmpPoint.y + ';'
            }
            if (path.rectPoint1) {
              tmpPoint = path.pointToLocal(path.rectPoint1.segments[0].point)
              x += tmpPoint.x + ';'
              y += tmpPoint.y + ';'
            }
            let params = {
              mMarkAnnotationId: path.mMarkAnnotationId,
              x: x.slice(0, x.length - 1),
              y: y.slice(0, y.length - 1),
            }
            updateAnnotation(params)
          } else if (
            tmp.type === 'path' &&
            tmp.info.mMarkAnnotationId &&
            (tmp.info.markType === 'poly' || tmp.info.markType === 'line')
          ) {
            let path = tmp.info
            let x = '',
              y = ''
            for (let i = 0; i < path.segments.length; i++) {
              let tmpPoint = path.pointToLocal(path.segments[i].point)
              x += tmpPoint.x + ';'
              y += tmpPoint.y + ';'
            }
            let params = {
              mMarkAnnotationId: path.mMarkAnnotationId,
              x: x.slice(0, x.length - 1),
              y: y.slice(0, y.length - 1),
            }
            updateAnnotation(params)
          }
        } else if (tmp.action && tmp.action === 'delete') {
          let params = {
            mMarkAnnotationId: tmp.info.mMarkAnnotationId,
          }
          deleteAnnotation(params)
        } else if (tmp.action && tmp.action === 'addNew') {
          const annotation = tmp.info.toDBObject()
          if (annotation.mQaResultId) {
            addAnnotation(annotation)
          } else {
            this.msgError('页面加载异常，请重新标注。')
          }
        }
        this.changeList.remove(key)
      }
      this.changeList.clear()
      this.removeFrameCache(this.fileList[this.currentIndex].mQaResultId)
      this.changePage(0)
    },

    copyPreviousAnnotation() {
      if (this.currentIndex > 0) {
        console.log(this.fileList[this.currentIndex - 1].mQaResultId, this.fileList[this.currentIndex].mQaResultId)
        copyPreviousAnnotation(
          this.fileList[this.currentIndex - 1].mQaResultId,
          this.fileList[this.currentIndex].mQaResultId
        ).then((ret) => {
          if (ret.code == 200) {
            this.removeFrameCache(this.fileList[this.currentIndex].mQaResultId)
            this.changePage(0)
            this.msgSuccess(ret.msg)
          } else {
            this.msgError(ret.msg)
          }
        })
      }
    },

    handleSelectedAnnoChange(anno) {
      // TODO: highlight paper path
      console.log(anno)
      this.selectedAnno = anno
    },
    handleSelectionAnnoChange(ids, items, filter) {
      console.log(ids)
      this.selectedIds = ids
      this.viewAnnotations = items
    },
  },
  /**
   * end region <<< annotation
   * ************************************************************
   */

  // 捕获界面刷新事件，清除标注结果缓存
  // beforeRouteEnter(to, from, next) {
  //   next((vm) => {
  //     if (from.name === null) {
  //       vm.removeAnnotationCache('MA:' + to.query.mQaResultId)
  //     }
  //   })
  // },

  created() {
    const routeQuery = this.$route.query
    if (!routeQuery.taskId || !routeQuery.mQaResultId) this.$router.go(-1)

    this.encoder = new window.Whammy.Video(5)
    const { taskId, mQaResultId } = routeQuery
    this.queryParams = { taskId, mQaResultId }
    this.selectedObjectA1 = routeQuery.selectedObjectA1

    this.$store.dispatch('app/closeSideBar', { withoutAnimation: true })

    window.addEventListener('contextmenu', this.contextmenuHandler, false)
    //在页面刷新时将paperToolPosition的信息保存到localStorage里
    window.addEventListener('beforeunload', () => {
      localStorage.setItem('currentIndex', this.currentIndex)
    })
  },
  mounted() {
    this.qaTaskQuery()
    window.addEventListener('resize', this.onWindowResize, false)
    document.addEventListener('keydown', this.keyDownHandler, false)
    screenfull.on('change', (event) => {
      this.toolbar.fullscreen = screenfull.isFullscreen
    })
  },
  destroyed() {
    this.cacheStatus = {
      taskId: null,
      index: 0,
      version: 0,
    }
    cancelAnimationFrame(this.animationFrameId)
    window.removeEventListener('contextmenu', this.contextmenuHandler, false)
    window.removeEventListener('resize', this.onWindowResize, false)
    document.removeEventListener('keydown', this.keyDownHandler, false)

    this.$store.dispatch('app/toggleSideBar')
  },
}
</script>
<style lang="scss" scoped>
.online-editor {
  // position: relative;
  background-color: Gainsboro;
  width: 100%;
  height: 100%;

  .header-toobox {
    padding: 8px 10px;
    background: #fff;
    border: 1px solid #e6ebf5;

    .input-search {
      width: 300px;
      /deep/ .el-input-group__append {
        color: white;
        background: #1890ff;
        border-color: #1890ff;
      }
    }

    .header-frame-info {
      line-height: 28px;
      .info-item {
        padding: 0 8px;
        display: inline-block;
        font-weight: 400;
        color: #606266;
      }
    }
  }

  .content-container {
    height: calc(100% - 113px);
  }
}
</style>
